from flask import Flask, request, render_template
from flask_cors import CORS
from flask_mysqldb import MySQL
from random import random
import time
import base64
import rq_constant as rqc
import database_constant as dbc

app = Flask(__name__)
CORS(app)

win_constant = [[1024, 1024, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
lose_constant = [[2, 4, 8, 16], [16, 8, 4, 2], [2, 4, 8, 16], [4, 0, 2, 4]]


class Game2048:

    def __init__(self, user_id=None):
        self.grid_size = 4
        self.grid = [[0] * self.grid_size for _ in range(self.grid_size)]
        self.score = 0
        self.time_limit = 0  # 时间限制（秒）
        self.remaining_time = 0  # 剩余时间（秒）
        self.last_move_time = None  # 初始化时不设置开始时间
        self.user_id = user_id
        self.add_new_number()
        self.add_new_number()

    def add_new_number(self):
        empty_cells = [(i, j) for i in range(self.grid_size) for j in range(self.grid_size) if self.grid[i][j] == 0]
        if empty_cells:
            i, j = empty_cells[int(random() * len(empty_cells))]
            self.grid[i][j] = 2

    def rotate_grid(self, times=1):
        for _ in range(times):
            self.grid = [[self.grid[j][self.grid_size - 1 - i] for j in range(self.grid_size)] for i in
                         range(self.grid_size)]

    def move(self, direction):
        rotations = {'right': 2, 'up': 1, 'left': 0, 'down': 3}
        self.rotate_grid(rotations[direction])
        moved = self.merge_left()
        self.rotate_grid(4 - rotations[direction])
        if moved:
            self.add_new_number()
            # 每次有效移动都更新时间
            if self.time_limit > 0:
                self.last_move_time = time.time()
                # 更新剩余时间
                self.get_state()
        self.print_grid()
        return moved

    def merge_left(self):
        moved = False
        for i in range(self.grid_size):
            # 移除零并合并相同数字
            row = [x for x in self.grid[i] if x != 0]
            for j in range(len(row) - 1):
                if row[j] == row[j + 1]:
                    row[j] *= 2
                    self.score += row[j]
                    row[j + 1] = 0
            row = [x for x in row if x != 0]
            # 填充零
            new_row = row + [0] * (self.grid_size - len(row))
            if new_row != self.grid[i]:
                moved = True
            self.grid[i] = new_row
        return moved

    def is_game_over(self):
        if any(0 in row for row in self.grid):
            return False
        for i in range(self.grid_size):
            for j in range(self.grid_size - 1):
                if self.grid[i][j] == self.grid[i][j + 1]:
                    return False
        for j in range(self.grid_size):
            for i in range(self.grid_size - 1):
                if self.grid[i][j] == self.grid[i + 1][j]:
                    return False
        return True

    def print_grid(self):
        print(f'{self.user_id}的当前网格状态：')
        print('-' * (self.grid_size * 6 + 1))
        for row in self.grid:
            print('|', end=' ')
            for cell in row:
                print(f'{cell:4}|', end=' ')
            print('\n' + '-' * (self.grid_size * 6 + 1))
        print(f'当前分数: {self.score}')

    def get_state(self):
        return {
            'user_id': self.user_id,
            'grid': self.grid,
            'score': self.score,
            'game_over': self.is_game_over(),
        }

    def win(self):
        self.grid = win_constant

    def lose(self):
        self.grid = lose_constant


def encode_string(string):
    if string is None or len(string) == 0:
        return ''
    else:
        name_bytes = string.encode('utf-8')
        name_base64 = base64.b64encode(name_bytes).decode('utf-8')
        return name_base64


def decode_str(string):
    if string is None or len(string) == 0:
        return ''
    else:
        return base64.b64decode(string).decode('utf-8')


use_local = dbc.use_local

config = {
    'MYSQL_HOST': dbc.MYSQL_HOST if use_local else dbc.REMOTE_MYSQL_HOST,
    'MYSQL_PORT': dbc.MYSQL_PORT if use_local else dbc.REMOTE_MYSQL_PORT,
    'MYSQL_USER': dbc.MYSQL_USER if use_local else dbc.REMOTE_MYSQL_USER,
    'MYSQL_PASSWORD': dbc.MYSQL_PASSWORD if use_local else dbc.REMOTE_MYSQL_PASSWORD,
    'MYSQL_DB': dbc.MYSQL_DB if use_local else dbc.REMOTE_MYSQL_DB,
    'MYSQL_CURSORCLASS': dbc.MYSQL_CURSORCLASS if use_local else dbc.REMOTE_MYSQL_CURSORCLASS,
}
app.config.update(config)
mysql = MySQL(app)

# 创建游戏实例字典，用于存储不同用户的游戏状态
games = {}


# 获取或创建用户的游戏实例
def get_or_create_game(user_id):
    if user_id not in games:
        games[user_id] = Game2048(user_id)
    return games[user_id]


# 删除用户的游戏实例
def delete_game(user_id):
    if user_id in games:
        del games[user_id]


@app.route('/web')
def index():
    return render_template('index.html')


@app.route('/find')
def find():
    # 从查询参数获取name值
    name = request.args.get('name')

    try:
        decode = base64.b64decode(name)
        decode_name = decode.decode('utf-8')
    except Exception as e:
        return rqc.error_parameters('name')

    # 验证必填参数
    if not name:
        return rqc.error_parameters('name')

    cur = mysql.connection.cursor()

    try:
        # 使用参数化查询防止SQL注入
        cur.execute("select * from db2048.user where name = %s", (decode_name,))
        data = cur.fetchall()

        if not data:
            return rqc.not_found('name')

        return rqc.success(data)
    except Exception as e:
        print(e)
        return rqc.server_error()

    finally:
        cur.close()


@app.route('/isGaming')
def is_gaming():
    name = request.args.get('name')
    try:
        decode_name = decode_str(name)
    except Exception as e:
        return rqc.error_parameters('name')
    # 验证必填参数
    if not name:
        return rqc.error_parameters('name')
    try:
        game = games[decode_name]
        return rqc.success(game.get_state())
    except Exception as e:
        print(e)
        return rqc.server_error()


@app.route('/register', methods=['PUT'])
def register():
    name = request.args.get('name')
    if not name:
        return rqc.error_parameters('name')

    try:
        decode = base64.b64decode(name)
        decode_name = decode.decode('utf-8')
    except Exception as e:
        return rqc.error_parameters('name')

    cur = None
    try:
        cur = mysql.connection.cursor()

        # Check if user exists
        query = "select count(*) from db2048.user where name = %s"
        cur.execute(query, (decode_name,))
        # 获取结果
        result = cur.fetchone()['count(*)']
        if result > 0:
            return rqc.exist_data(decode_name + "name")
        else:

            if len(decode_name) > 7 or len(decode_name) < 1:
                return rqc.error_parameters(decode_name)
            insert_query = "insert into db2048.user (name) VALUES (%s)"
            cur.execute(insert_query, (decode_name,))
            mysql.connection.commit()
            return rqc.success()
    except Exception as e:
        print(e)
        return rqc.server_error()
    finally:
        if cur:
            cur.close()


@app.route('/new_game', methods=['POST'])
def new_game():
    try:
        session_id = decode_str(request.json.get('session_id'))
        time_limit = request.json.get('time_limit', 0)

        if not session_id:
            return rqc.error_parameters('session_id')

        if session_id in games:
            del games[session_id]

        game = Game2048(user_id=session_id)
        game.time_limit = time_limit
        game.remaining_time = time_limit
        game.last_move_time = time.time() if time_limit > 0 else None
        games[session_id] = game

        return rqc.success(games[session_id].get_state())
    except Exception as e:
        return rqc.server_error()


@app.route('/move', methods=['POST'])
def move():
    session_id = decode_str(request.json.get('session_id'))
    direction = request.json.get('direction')
    if not session_id or not direction:
        return rqc.error_parameters('session_id')

    if session_id not in games:
        return rqc.not_found('session_id')

    if direction not in ['up', 'down', 'left', 'right']:
        return rqc.error_parameters('direction')

    game = games[session_id]
    if not game.is_game_over():
        game.move(direction)
    return rqc.success(game.get_state())


# 测试,直接返回一个能赢的二维数组
@app.route('/win', methods=['POST'])
def test():
    session_id = request.json.get('session_id')
    decode_id = decode_str(session_id)
    if not decode_id or decode_id not in games:
        return rqc.error_parameters('session_id')
    game = games[decode_id]
    game.win()
    return rqc.success(game.get_state())


@app.route('/lose', methods=['POST'])
def lose():
    session_id = request.json.get('session_id')
    decode_id = decode_str(session_id)
    if not decode_id or decode_id not in games:
        return rqc.error_parameters('session_id')
    game = games[decode_id]
    game.lose()
    return rqc.success(game.get_state())


if __name__ == '__main__':
    app.run(debug=True)
